Completed
Pull Request — master (#99)
by Ruben de
01:04
created

BtccomConverter.convertBlocks   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
dl 0
loc 10
rs 9.4285
nop 1
1
var RestClient = require('./rest_client');
2
var Wallet = require('./wallet');
3
var blocktrail = require('./blocktrail');
4
var bitcoinJS = require('bitcoinjs-lib');
5
6
var BtccomConverter = function(network, useNewCashAddr, rawTxHost) {
7
    this.network = network;
8
    this.useNewCashAddr = useNewCashAddr;
9
10
    this.specialRawTxClientOnly = new RestClient({
11
        host: rawTxHost,
12
        https: true,
13
        port: 443,
14
        endpoint: "/"
15
    });
16
};
17
18
function getAddressScriptInfo(address, network, useNewCashAddr) {
19
    var addressScriptInfo;
20
21
    try {
22
        addressScriptInfo = bitcoinJS.address.toOutputScript(address, network, useNewCashAddr);
23
    } catch (e) {
24
        addressScriptInfo = null;
25
    }
26
    return addressScriptInfo;
27
}
28
29
function getScriptAsm(chunks) {
30
    var scriptAsm;
31
32
    try {
33
        scriptAsm = bitcoinJS.script.toASM(chunks);
34
    } catch (e) {
35
        scriptAsm = null;
36
    }
37
    return scriptAsm;
38
}
39
40
function prettifyAsm(asm) {
41
    if (!asm) {
42
        return asm;
43
    }
44
45
    return asm.replace(/^0 /, "OP_0 ");
46
}
47
48
function getType(script) {
49
    var type;
50
51
    try {
52
        type = bitcoinJS.script.classifyOutput(script);
53
    } catch (e) {
54
        type = null;
55
    }
56
    return type;
57
}
58
59
function getBase58AddressHash160(address, network, useNewCashAddr) {
60
    var addressInfo;
61
    try {
62
        addressInfo = Wallet.getAddressAndType(address, network, useNewCashAddr);
63
    } catch (e) {
64
        return null;
65
    }
66
67
    if (addressInfo.type === "base58") {
68
        return addressInfo.decoded.hash;
69
    } else if (addressInfo.type === "bech32") {
70
        if (addressInfo.data.length === 20) {
71
            return addressInfo.decoded.hash;
72
        }
73
        return null;
74
    } else if (addressInfo.type === "") {
75
        return addressInfo.decoded.hash;
76
    }
77
78
    return null;
79
}
80
81
function convertBtccomOutputScriptType(scriptType) {
82
    switch (scriptType) {
83
        case "P2PKH_PUBKEY":
84
            return "pubkey";
85
        case "P2PKH":
86
            return "pubkeyhash";
87
        case "P2SH":
88
            return "scripthash";
89
        case "P2WSH_V0":
90
            return "witnessscripthash";
91
        case "P2WPKH":
92
            return "witnesspubkeyhash";
93
        case "NULL_DATA":
94
            return "op_return";
95
        case "coinbase":
96
            return "coinbase";
97
        default:
98
            throw new Error("Not implemented yet, script type: " + scriptType);
99
    }
100
}
101
102
function utcTimestampToISODateStr(time) {
103
    return (new Date(time * 1000)).toISOString().replace(/\.000Z$/, '+0000');
104
}
105
106
function flattenAddresses(addrs) {
107
    if (!addrs) {
108
        return addrs;
109
    } else if (addrs.length === 1) {
110
        return addrs[0];
111
    } else {
112
        return addrs;
113
    }
114
}
115
116
function convertBtccomTxToBlocktrail(tx) {
117
    /* jshint -W071, -W074 */
118
    var data = {};
119
120
    data.size = tx.vsize;
121
    data.hash = tx.hash;
122
    data.block_height = tx.block_height ;
123
    data.block_time = utcTimestampToISODateStr(tx.block_time);
124
    data.block_hash = null; // TODO: missing block_hash
125
    data.confirmations = tx.confirmations;
126
    data.is_coinbase = tx.is_coinbase;
127
128
    var totalInputValue;
129
    if (data.is_coinbase) {
130
        totalInputValue = tx.outputs[0].value - tx.fee;
131
    } else {
132
        totalInputValue = tx.inputs_value;
133
    }
134
135
    data.total_input_value = totalInputValue;
136
    data.total_output_value = tx.outputs.reduce(function(total, output) {
137
        return total + output.value;
138
    }, 0);
139
    data.total_fee = tx.fee;
140
    data.inputs = [];
141
    data.outputs = [];
142
    data.opt_in_rbf = false;
143
144
    for (var inputIdx in tx.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
145
        var input = tx.inputs[inputIdx];
146
        var scriptType;
147
        var inputValue;
148
        var inputTxid;
149
        var outpointIdx;
150
151
        if (input.sequence < bitcoinJS.Transaction.DEFAULT_SEQUENCE - 1) {
152
            data.opt_in_rbf = true;
153
        }
154
155
        if (data.is_coinbase && input.prev_position === -1 &&
156
            input.prev_tx_hash === "0000000000000000000000000000000000000000000000000000000000000000") {
157
            scriptType = "coinbase";
158
            inputTxid = null;
159
            inputValue = totalInputValue;
160
            outpointIdx = 0xffffffff;
161
        } else {
162
            scriptType = input.prev_type;
163
            inputValue = input.prev_value;
164
            inputTxid = input.prev_tx_hash;
165
            outpointIdx = input.prev_position;
166
        }
167
168
        data.inputs.push({
169
            index: parseInt(inputIdx, 10),
170
            output_hash: inputTxid,
171
            output_index: outpointIdx,
172
            value: inputValue,
173
            sequence: input.sequence,
174
            address: flattenAddresses(input.prev_addresses),
175
            type: convertBtccomOutputScriptType(scriptType),
176
            script_signature: input.script_hex
177
        });
178
    }
179
180
    for (var outIdx in tx.outputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
181
        var output = tx.outputs[outIdx];
182
183
        data.outputs.push({
184
            index: parseInt(outIdx, 10),
185
            value: output.value,
186
            address: flattenAddresses(output.addresses),
187
            type: convertBtccomOutputScriptType(output.type),
188
            script: prettifyAsm(output.script_asm),
189
            script_hex: output.script_hex,
190
            spent_hash: output.spent_by_tx,
191
            spent_index: output.spent_by_tx_position
192
        });
193
    }
194
195
    data.size = tx.size;
196
    data.is_double_spend = tx.is_double_spend;
197
198
    data.lock_time_timestamp = null;
199
    data.lock_time_block_height = null;
200
    if (tx.lock_time) {
201
        if (tx.lock_time < blocktrail.LOCK_TIME_TIMESTAMP_THRESHOLD) {
202
            data.lock_time_block_height = tx.lock_time;
203
        } else {
204
            data.lock_time_timestamp = tx.lock_time;
205
        }
206
    }
207
208
    // Extra fields from Btc.com
209
    data.is_sw_tx = tx.is_sw_tx;
210
    data.weight = tx.weight;
211
    data.witness_hash = tx.witness_hash;
212
    data.lock_time  = tx.lock_time;
213
    data.sigops = tx.sigops;
214
    data.version = tx.version;
215
216
    return data;
217
}
218
219
BtccomConverter.prototype.paginationParams = function(params) {
220
    if (!params) {
221
        return params;
222
    }
223
224
    if (typeof params.limit !== "undefined") {
225
        params.pagesize = params.limit;
226
        delete params.limit;
227
    }
228
229
    return params;
230
};
231
232
BtccomConverter.prototype.getUrlForBlock = function(blockHash) {
233
    return "/block/" + blockHash;
234
};
235
236
BtccomConverter.prototype.getUrlForTransaction = function(txId) {
237
    return "/tx/" + txId + "?verbose=3";
238
};
239
240
BtccomConverter.prototype.getUrlForTransactions = function(txIds) {
241
    return "/tx/" + txIds.join(",") + "?verbose=3";
242
};
243
244
BtccomConverter.prototype.getUrlForBlockTransaction = function(blockHash) {
245
    return "/block/" + blockHash + "/tx";
246
};
247
248
BtccomConverter.prototype.getUrlForAddress = function(address) {
249
    return "/address/" + address;
250
};
251
252
BtccomConverter.prototype.getUrlForAddressTransactions = function(address) {
253
    return "/address/" + address + "/tx?verbose=3";
254
};
255
256
BtccomConverter.prototype.getUrlForAddressUnspent = function(address) {
257
    return "/address/" + address + "/unspent";
258
};
259
260
BtccomConverter.prototype.getUrlForAllBlocks = function() {
261
    return "/block/list";
262
};
263
264
BtccomConverter.prototype.handleErros = function(self, data) {
265
    if (data.err_no === 0 || data.data !== null) {
266
        return data;
267
    } else {
268
        return {
269
            err_no: data.err_no,
270
            err_msg: data.err_msg,
271
            data: data.data
272
        };
273
    }
274
};
275
276
BtccomConverter.prototype.convertBlock = function(oldData) {
277
    var data = {
278
        hash: oldData.hash,
279
        version: oldData.version,
280
        height: oldData.height,
281
        block_time: utcTimestampToISODateStr(oldData.timestamp),
282
        arrival_time: utcTimestampToISODateStr(oldData.timestamp),
283
        bits: oldData.bits,
284
        nonce: oldData.nonce,
285
        merkleroot: oldData.mrkl_root,
286
        prev_block: oldData.prev_block_hash,
287
        next_block: oldData.next_block_hash,
288
        byte_size: oldData.stripped_size,
289
        difficulty: Math.floor(oldData.difficulty),
290
        transactions: oldData.tx_count,
291
        reward_block: oldData.reward_block,
292
        reward_fees: oldData.reward_fees,
293
        created_at: oldData.created_at,
294
        confirmations: oldData.confirmations,
295
        is_orphan: oldData.is_orphan,
296
        is_sw_block: oldData.is_sw_block,
297
        weight: oldData.weight,
298
        miningpool_name: oldData.miningpool_name || null,
299
        miningpool_url: oldData.miningpool_url || null,
300
        miningpool_slug: oldData.miningpool_slug || null
301
    };
302
303
    return data;
304
};
305
306
BtccomConverter.prototype.convertBlocks = function(oldData) {
307
    var self = this;
0 ignored issues
show
Unused Code introduced by
The variable self seems to be never used. Consider removing it.
Loading history...
308
309
    return {
310
        data: oldData.data.list,
311
        current_page: oldData.data.page,
312
        per_page: oldData.data.pagesize,
313
        total: oldData.data.total_count
314
    };
315
};
316
317
BtccomConverter.prototype.convertBlockTxs = function(oldData) {
318
    var list = [];
319
    oldData.data.list.forEach(function(oldTx) {
320
        var resTx = convertBtccomTxToBlocktrail(oldTx);
321
322
        list.push(resTx);
323
    });
324
325
    return {
326
        data: list,
327
        current_page: oldData.data.page,
328
        per_page: oldData.data.pagesize,
329
        total: oldData.data.total_count
330
    };
331
};
332
333
BtccomConverter.prototype.convertTx = function(oldData, rawTx) {
334
    var data = convertBtccomTxToBlocktrail(oldData.data);
335
    data.raw = rawTx;
336
    return data;
337
};
338
339
BtccomConverter.prototype.convertTxs = function(oldData) {
340
    var res = {};
341
342
    oldData.data
343
        .filter(function(tx) { return !!tx; })
344
        .forEach(function(oldTx) {
345
            var tx = convertBtccomTxToBlocktrail(oldTx);
346
            res[tx.hash] = tx;
347
        });
348
349
    return {data: res};
350
};
351
352
BtccomConverter.prototype.convertAddressTxs = function(oldData) {
353
    var data = oldData.data.list.map(function(tx) {
354
        var res = {};
355
356
        res.hash = tx.hash;
357
        res.time = utcTimestampToISODateStr(tx.block_time);
358
        res.confirmations = tx.confirmations;
359
        res.block_height = tx.block_height;
360
        res.block_hash = null; // @TODO: missing block_hash
361
        res.is_coinbase = tx.is_coinbase;
362
        res.total_input_value = tx.inputs_value;
363
        res.total_output_value = tx.outputs_value;
364
        res.total_fee = tx.fee;
365
366
        res.inputs = tx.inputs.map(function(input, inIdx) {
367
            return {
368
                index: inIdx,
369
                output_hash: input.prev_tx_hash,
370
                output_index: input.prev_position,
371
                value: input.prev_value,
372
                address: flattenAddresses(input.prev_addresses),
373
                type:  res.is_coinbase ? res.is_coinbase : convertBtccomOutputScriptType(input.prev_type),
374
                script_signature: input.script_hex
375
            };
376
        });
377
378
        res.outputs = tx.outputs.map(function(output, outIdx) {
379
            return {
380
                index: outIdx,
381
                value: output.value,
382
                address: flattenAddresses(output.addresses),
383
                type: convertBtccomOutputScriptType(output.type),
384
                script: prettifyAsm(output.script_asm),
385
                spent_hash: output.spent_by_tx || null,
386
                script_hex: output.script_hex,
387
                spent_index: output.spent_by_tx_position
388
            };
389
        });
390
391
        // Extra fields from Btc.com
392
        res.is_double_spend = tx.is_double_spend;
393
        res.is_sw_tx = tx.is_sw_tx;
394
        res.weight = tx.weight;
395
        res.witness_hash = tx.witness_hash;
396
        res.version = tx.version;
397
398
        return res;
399
    });
400
401
    return {
402
        data: data,
403
        current_page: oldData.data.page,
404
        per_page: oldData.data.pagesize,
405
        total: oldData.data.total_count
406
    };
407
};
408
409
BtccomConverter.prototype.convertAddress = function(oldData) {
410
    var data = {};
411
412
    data.address = oldData.data.address;
413
    data.hash160 = getBase58AddressHash160(oldData.data.address, this.network, this.useNewCashAddr).toString("hex").toUpperCase();
414
    data.balance = oldData.data.balance;
415
    data.received = oldData.data.received;
416
    data.sent = oldData.data.sent;
417
    data.transactions = oldData.data.tx_count;
418
    data.utxos = oldData.data.unspent_tx_count;
419
    data.unconfirmed_received = oldData.data.unconfirmed_received;
420
    data.unconfirmed_sent = oldData.data.unconfirmed_sent;
421
    data.unconfirmed_transactions = oldData.data.unconfirmed_tx_count;
422
    data.first_tx = oldData.data.first_tx;
423
    data.last_tx = oldData.data.last_tx;
424
425
    return data;
426
};
427
428
BtccomConverter.prototype.convertAddressUnspentOutputs = function(oldData, address) {
429
    var script = getAddressScriptInfo(address, this.network, this.useNewCashAddr);
430
    var script_hex = script.toString("hex");
431
    var script_asm = getScriptAsm(script);
432
    var type = getType(script);
433
    var data = oldData.data.list.map(function(utxo) {
434
        return {
435
            hash: utxo.tx_hash,
436
            confirmations: utxo.confirmations,
437
            value: utxo.value,
438
            index: utxo.tx_output_n,
439
            address: address,
440
            type: type,
441
            script: script_asm,
442
            script_hex: script_hex
443
        };
444
    });
445
446
    return {
447
        data: data,
448
        current_page: oldData.data.page,
449
        total: oldData.data.total_count
450
    };
451
};
452
453
exports = module.exports = BtccomConverter;
454